package dao

import (
	"errors"
	"net/http/httptest"
	"sync"
	"time"

	"gin-gateway/core/init"
	"gin-gateway/core/dto"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type App struct {
	ID        int64     `json:"id" gorm:"primary_key"`
	AppID     string    `json:"app_id" gorm:"column:app_id" description:"租户id	"`
	Name      string    `json:"name" gorm:"column:name" description:"租户名称	"`
	Secret    string    `json:"secret" gorm:"column:secret" description:"密钥"`
	WhiteIPS  string    `json:"white_ips" gorm:"column:white_ips" description:"ip白名单，支持前缀匹配"`
	Qpd       int64     `json:"qpd" gorm:"column:qpd" description:"日请求量限制"`
	Qps       int64     `json:"qps" gorm:"column:qps" description:"每秒请求量限制"`
	CreatedAt time.Time `json:"create_at" gorm:"column:create_at" description:"添加时间	"`
	UpdatedAt time.Time `json:"update_at" gorm:"column:update_at" description:"更新时间"`
	IsDelete  int8      `json:"is_delete" gorm:"column:is_delete" description:"是否已删除；0：否；1：是"`
}

func (t *App) TableName() string {
	return "gateway_app"
}

func (t *App) Find(c *gin.Context, tx *gorm.DB, search *App) (*App, error) {
	model := &App{}
	err := tx.WithContext(Init.NewTraceContext(c)).Where(search).Find(model).Error
	return model, err
}

func (t *App) Save(c *gin.Context, tx *gorm.DB) error {
	if err := tx.WithContext(Init.NewTraceContext(c)).Save(t).Error; err != nil {
		return err
	}
	return nil
}

func (t *App) APPList(c *gin.Context, tx *gorm.DB, params *dto.APPListInput) ([]App, int64, error) {
	var list []App
	var count int64
	pageNo := params.PageNo
	pageSize := params.PageSize

	//limit offset,pagesize
	offset := (pageNo - 1) * pageSize
	query := tx.WithContext(Init.NewTraceContext(c))
	query = query.Table(t.TableName()).Select("*")
	query = query.Where("is_delete=?", 0)
	if params.Info != "" {
		query = query.Where(" (name like ? or app_id like ?)", "%"+params.Info+"%", "%"+params.Info+"%")
	}
	err := query.Limit(pageSize).Offset(offset).Order("id desc").Find(&list).Error
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, 0, err
	}
	errCount := query.Count(&count).Error
	if errCount != nil {
		return nil, 0, err
	}
	return list, count, nil
}

var AppManagerHandler *AppManager

func init() {
	AppManagerHandler = NewAppManager()
}

type AppManager struct {
	AppMap   map[string]*App // key =AppID ,v = AppInfo
	AppSlice []*App
	Locker   sync.RWMutex
	init     sync.Once
	err      error
}

func NewAppManager() *AppManager {
	return &AppManager{
		AppMap:   map[string]*App{},
		AppSlice: []*App{},
		Locker:   sync.RWMutex{},
		init:     sync.Once{},
	}
}

func (a *AppManager) GetAppList() []*App {
	return a.AppSlice
}

func (a *AppManager) InAppMap(key string) (*App, bool) {
	app, exists := a.AppMap[key]
	return app, exists
}

// once 必须配合单例使用，否则没有效果.。取出所有app 数据，放入AppManager
func (a *AppManager) LoadOnce() error {
	a.init.Do(func() {
		ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
		tx, err := Init.GetGormPool("default_db")
		if err != nil {
			a.err = err
			return
		}

		appInfo := &App{}
		appList, _, err := appInfo.APPList(ctx, tx, &dto.APPListInput{PageNo: 1, PageSize: 99999})
		if err != nil {
			a.err = err
			return
		}

		a.Locker.Lock()
		defer a.Locker.Unlock()
		for i := range appList {
			a.AppMap[appList[i].AppID] = &appList[i]
			a.AppSlice = append(a.AppSlice, &appList[i])
		}
	})
	return a.err
}
